package org.sky.cloud.common.notify.publisher;

import cn.hutool.core.collection.ConcurrentHashSet;
import org.sky.cloud.common.notify.Event;
import org.sky.cloud.common.notify.listener.Subscriber;
import org.sky.cloud.common.utils.StringUtils;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;

public class DefaultEventPublisher extends Thread implements EventPublisher{


    private volatile boolean initialized = false;

    private volatile boolean shutdown = false;

    private final ConcurrentHashSet<Subscriber> subscribers = new ConcurrentHashSet<>();

    private BlockingQueue<Event> events;


    @Override
    public void init(Class<?> type, int queueSize) {
        setDaemon(true);
        this.events = new ArrayBlockingQueue<>(queueSize);
        start();
    }


    @Override
    public synchronized void start() {
        if(!initialized){
            super.start();
            initialized = true;
        }
    }


    @Override
    public void run() {
        handleEvent();
    }

    private void handleEvent() {
        try {
            while (!shutdown){
                final Event event = events.take();
                receiveEvent(event);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void receiveEvent(Event event) {
        final long sequence = event.sequence();
        for(Subscriber subscriber : subscribers){
            if(subscriber.subscribeType().equals(event.getClass())){
                 notifySubscriber(subscriber, event);
            }
        }
    }

    @Override
    public void addSubscriber(Subscriber subscriber) {
        subscribers.add(subscriber);
    }

    @Override
    public void removeSubscriber(Subscriber subscriber) {
        subscribers.remove(subscriber);
    }

    @Override
    public boolean publish(Event event) {
        boolean success = events.offer(event);
        if(!success){
            receiveEvent(event);
        }
        return success;
    }


    @Override
    public void notifySubscriber(Subscriber subscriber, Event event) {
        final Runnable job = () -> subscriber.onEvent(event);
        Executor executor = subscriber.getExecutor();
        if(executor != null){
            executor.execute(job);
        }else{
            job.run();
        }
    }

    @Override
    public void shutdown() throws Exception {
        this.shutdown = true;
        this.events.clear();
    }
}
